package com.alibaba.cloud.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.cloud.enums.CustomerHttpMethod;
import com.alibaba.cloud.enums.CustomerMediaType;
import com.google.gson.Gson;


/**
 * The Class HttpUtil.
 *
 * @ClassName: HttpUtil
 * @Description:原生方式调用 
 * @author: zhb1316783262@163.com
 * @date: 2020年3月26日 下午3:43:01
 */
public class HttpUtil {
    
    /** The Constant log. */
    private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);
    
    /** The Constant CONNECT_TIMEOUT. */
    private static final int CONNECT_TIMEOUT = 2000;
    
    /** The Constant READ_TIMEOUT. */
    private static final int READ_TIMEOUT = 3000;
    
    /** The Constant CHARSET. */
    private static final String CHARSET = "UTF-8";

    /**
     * Post.
     *
     * @param requestUrl the request url
     * @param params  参数
     * @param headers 头信息
     * @param mediaType the media type
     * @return the string
     * @throws KeyManagementException the key management exception
     * @throws NoSuchAlgorithmException the no such algorithm exception
     * @throws NoSuchProviderException the no such provider exception
     * @throws IOException Signals that an I/O exception has occurred.
     * @MethodName: post
     * @Description: POST方法提交Http请求，语义为“增加” <br/>
     *               注意：Http方法中只有POST方法才能使用body来提交内容
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:29:32
     */
    public static String post(String requestUrl, Map<String, Object> params, Map<String, String> headers,
            CustomerMediaType mediaType)
            throws KeyManagementException, NoSuchAlgorithmException, NoSuchProviderException, IOException {
        if (requestUrl.toLowerCase().startsWith("https")) {
            invokeSSLUrl(requestUrl, params, headers, CustomerHttpMethod.POST, mediaType);
        }
        return invokeUrl(requestUrl, params, headers, CustomerHttpMethod.POST, mediaType);
    }

    /**
     * Gets the.
     *
     * @param requestUrl the request url
     * @param params  参数
     * @param headers 头信息
     * @return the string
     * @throws KeyManagementException the key management exception
     * @throws NoSuchAlgorithmException the no such algorithm exception
     * @throws NoSuchProviderException the no such provider exception
     * @throws IOException Signals that an I/O exception has occurred.
     * @MethodName: get
     * @Description: GET方法提交Http请求，语义为“查询”
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:28:12
     */
    public static String get(String requestUrl, Map<String, Object> params, Map<String, String> headers)
            throws KeyManagementException, NoSuchAlgorithmException, NoSuchProviderException, IOException {
        if (requestUrl.toLowerCase().startsWith("https")) {
            invokeSSLUrl(requestUrl, params, headers, CustomerHttpMethod.GET, null);
        }
        return invokeUrl(requestUrl, params, headers, CustomerHttpMethod.GET, null);
    }

    /**
     * Put.
     *
     * @param url    资源路径（如果url中已经包含参数，则params应该为null）
     * @param params 参数
     * @return the string
     * @MethodName: put
     * @Description: PUT方法提交Http请求，语义为“更改” <br/>
     *               注意：PUT方法也是使用url提交参数内容而非body，所以参数最大长度收到服务器端实现的限制，Resin大概是8K
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:26:30
     */
    public static String put(String url, Map<String, Object> params) {
        return invokeUrl(url, params, null, CustomerHttpMethod.PUT, null);
    }

    /**
     * Put.
     *
     * @param url     资源路径（如果url中已经包含参数，则params应该为null）
     * @param params  参数
     * @param headers 请求头参数
     * @return the string
     * @MethodName: put
     * @Description: PUT方法提交Http请求，语义为“更改”
     *               注意：PUT方法也是使用url提交参数内容而非body，所以参数最大长度收到服务器端实现的限制，Resin大概是8K
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:25:38
     */
    public static String put(String url, Map<String, Object> params, Map<String, String> headers) {
        return invokeUrl(url, params, headers, CustomerHttpMethod.PUT, null);
    }

    /**
     * Delete.
     *
     * @param url    资源路径（如果url中已经包含参数，则params应该为null）
     * @param params 参数
     * @return the string
     * @MethodName: delete
     * @Description: DELETE方法提交Http请求，语义为“删除”
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:25:09
     */
    public static String delete(String url, Map<String, Object> params) {
        return invokeUrl(url, params, null, CustomerHttpMethod.DELETE, null);
    }

    /**
     * Delete.
     *
     * @param url     资源路径（如果url中已经包含参数，则params应该为null）
     * @param params  参数
     * @param headers 请求头参数
     * @return the string
     * @MethodName: delete
     * @Description: DELETE方法提交Http请求，语义为“删除”
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:24:32
     */
    public static String delete(String url, Map<String, Object> params, Map<String, String> headers) {
        return invokeUrl(url, params, headers, CustomerHttpMethod.DELETE, null);
    }

    /**
     * Head.
     *
     * @param url    资源路径（如果url中已经包含参数，则params应该为null）
     * @param params 参数
     * @return the string
     * @MethodName: head
     * @Description: HEAD方法提交Http请求，语义同GET方法 . 跟GET方法不同的是，用该方法请求，服务端不返回message
     *               body只返回头信息，能节省带宽
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:27:01
     */
    public static String head(String url, Map<String, Object> params) {
        return invokeUrl(url, params, null, CustomerHttpMethod.HEAD, null);
    }

    /**
     * Head.
     *
     * @param url     资源路径（如果url中已经包含参数，则params应该为null）
     * @param params  参数
     * @param headers 请求头参数
     * @return the string
     * @MethodName: head
     * @Description: HEAD方法提交Http请求，语义同GET方法 <br/>
     *               跟GET方法不同的是，用该方法请求，服务端不返回message body只返回头信息，能节省带宽
     * @Author: zhb1316783262@163.com
     * @Return: String
     * @Date: 2020年3月29日上午11:27:31
     */
    public static String head(String url, Map<String, Object> params, Map<String, String> headers) {
        return invokeUrl(url, params, headers, CustomerHttpMethod.HEAD, null);
    }

    /**
     * * 非https 调用.
     *
     * @param requestUrl the request url
     * @param params the params
     * @param headers the headers
     * @param method the method
     * @param mediaType the media type
     * @return the string
     */
    private static String invokeUrl(String requestUrl, Map<String, Object> params, Map<String, String> headers,
            CustomerHttpMethod method, CustomerMediaType mediaType) {
        // 1.非Post 请求 构造请求参数字符串
        StringBuilder requestStr = null;
        if (Objects.nonNull(params)) {
            requestStr = new StringBuilder();
            Set<Entry<String, Object>> entries = params.entrySet();
            for (Entry<String, Object> entry : entries) {
                String value = (entry.getValue() != null) ? (String.valueOf(entry.getValue())) : "";
                requestStr.append(entry.getKey() + "=" + value + "&");
            }
            // 只有POST方法才能通过OutputStream(即form的形式)提交参数
            if (method != CustomerHttpMethod.POST) {
                requestUrl += "?" + requestStr.toString();
            }
        }

        URL url = null;
        HttpURLConnection httpURLConnection = null;
        BufferedWriter out = null;
        BufferedReader in = null;
        try {
            // 创建和初始化连接
            url =  URI.create(requestUrl).toURL();
            httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestProperty("accept", "*/*");
            httpURLConnection.setRequestProperty("connection", "Keep-Alive");
            httpURLConnection.setRequestProperty("content-type", CustomerMediaType.APPLICATION_FORM_URLENCODED.value());
            httpURLConnection.setRequestMethod(method.toString());
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);
            // 设置连接超时时间
            httpURLConnection.setConnectTimeout(CONNECT_TIMEOUT);
            // 设置读取超时时间
            httpURLConnection.setReadTimeout(READ_TIMEOUT);
            // 指定请求header参数
            if (headers != null && headers.size() > 0) {
                Set<String> headerSet = headers.keySet();
                for (String key : headerSet) {
                    httpURLConnection.setRequestProperty(key, headers.get(key));
                }
            }
            if (method == CustomerHttpMethod.POST && "application/json".equalsIgnoreCase(mediaType.value())) {
                // 获取URLConnection对象对应的输出流
                if (Objects.nonNull(params)) {
                    String requestJson = new Gson().toJson(params);
                    out = new BufferedWriter(new OutputStreamWriter(httpURLConnection.getOutputStream(), CHARSET));
                    out.write(requestJson);
                    // flush输出流的缓冲
                    out.flush();
                }
            } else if (method == CustomerHttpMethod.POST && Objects.nonNull(requestStr)) {
                // 发送请求参数
                out = new BufferedWriter(new OutputStreamWriter(httpURLConnection.getOutputStream(), CHARSET));
                out.write(requestStr.toString());
                out.flush();
            }
            // 接收返回结果
            StringBuilder result = new StringBuilder();
            in = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), CHARSET));
            if (Objects.nonNull(in)) {
                String line = "";
                while (Objects.nonNull((line = in.readLine()))) {
                    result.append(line);
                }
            }
            return result.toString();
        } catch (Exception e) {
            log.error("调用rul失败！请求URL：{}，参数：{}", url, params, e);
            // 处理错误流，提高http连接被重用的几率
            try {
                byte[] buf = new byte[1024];
                InputStream es = httpURLConnection.getErrorStream();
                if (Objects.nonNull(es)) {
                    while (es.read(buf) > 0) {
                        ;
                    }
                    es.close();
                }
            } catch (Exception exception) {
                log.error("调用rul失败！请求URL：{}，参数：{}", url, params, exception);
            }
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 关闭连接
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }
        return null;
    }

    /**
     * Invoke SSL url.
     *
     * @param requestUrl the request url
     * @param params the params
     * @param headers the headers
     * @param method the method
     * @param mediaType the media type
     * @return the string
     * @throws NoSuchAlgorithmException the no such algorithm exception
     * @throws NoSuchProviderException the no such provider exception
     * @throws KeyManagementException the key management exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private static String invokeSSLUrl(String requestUrl, Map<String, Object> params, Map<String, String> headers,
            CustomerHttpMethod method, CustomerMediaType mediaType)
            throws NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException, IOException {
        // 1.若非Post 请求 构造请求参数字符串
        StringBuilder requestStr = null;
        if (Objects.nonNull(params)) {
            requestStr = new StringBuilder();
            Set<Entry<String, Object>> entries = params.entrySet();
            for (Entry<String, Object> entry : entries) {
                String value = (entry.getValue() != null) ? (String.valueOf(entry.getValue())) : "";
                requestStr.append(entry.getKey() + "=" + value + "&");
            }
            // 只有POST方法才能通过OutputStream(即form的形式)提交参数
            if (method != CustomerHttpMethod.POST) {
                requestUrl += "?" + requestStr.toString();
            }
        }
        PrintWriter out = null;
        BufferedReader in = null;
        HttpsURLConnection httpsURLConnection = null;
        try {
            // 创建和初始化连接
            URL httpsUrl = URI.create(requestUrl).toURL();
            SSLContext sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
            sslcontext.init(null, new TrustManager[] { new MyX509TrustManager() }, new java.security.SecureRandom());
            HostnameVerifier ignoreHostnameVerifier = new MyHostnameVerifier();
            HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
            HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
            httpsURLConnection = (HttpsURLConnection) httpsUrl.openConnection();
            httpsURLConnection.setRequestProperty("accept", "*/*");
            httpsURLConnection.setRequestProperty("content-type",
                    CustomerMediaType.APPLICATION_FORM_URLENCODED.value());
            httpsURLConnection.setRequestMethod(mediaType.toString());
            // 设置连接超时时间
            httpsURLConnection.setConnectTimeout(CONNECT_TIMEOUT);
            // 设置读取超时时间
            httpsURLConnection.setReadTimeout(READ_TIMEOUT);
            // 发送POST请求必须设置如下两行
            httpsURLConnection.setDoOutput(true);
            httpsURLConnection.setDoInput(true);
            httpsURLConnection.setInstanceFollowRedirects(false);
            // 指定请求header参数
            if (headers != null && headers.size() > 0) {
                Set<String> headerSet = headers.keySet();
                for (String key : headerSet) {
                    httpsURLConnection.setRequestProperty(key, headers.get(key));
                }
            }
            if (method == CustomerHttpMethod.POST && "application/json".equalsIgnoreCase(mediaType.value())) {
                // 获取URLConnection对象对应的输出流
                if (Objects.nonNull(params)) {
                    String requestJson = new Gson().toJson(params);
                    out = new PrintWriter(new OutputStreamWriter(httpsURLConnection.getOutputStream(), CHARSET));
                    // 发送请求参数
                    out.write(requestJson);
                    // flush输出流的缓冲
                    out.flush();
                }
            } else if (method == CustomerHttpMethod.POST && Objects.nonNull(requestStr)) {
                // 获取URLConnection对象对应的输出流
                out = new PrintWriter(new OutputStreamWriter(httpsURLConnection.getOutputStream(), CHARSET));
                // 发送请求参数
                out.write(requestStr.toString());
                // flush输出流的缓冲
                out.flush();
            }
            // 接收返回结果
            StringBuilder result = new StringBuilder();
            in = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream(), CHARSET));
            if (in != null) {
                String line = "";
                while ((line = in.readLine()) != null) {
                    result.append(line);
                }
            }
            return result.toString();
        } catch (IOException ioException) {
            log.error("调用地址: {} 失败！请求参数: {}", requestUrl, params, ioException);
            // 处理错误流，提高http连接被重用的几率
            try {
                byte[] buf = new byte[1024];
                InputStream es = httpsURLConnection.getErrorStream();
                if (es != null) {
                    while (es.read(buf) > 0) {
                        ;
                    }
                    es.close();
                }
            } catch (IOException exception) {
                log.error("获取workId 异常,{}", exception);
                throw exception;
            }
        } finally {
            if (out != null) {
                out.close();
            }
            if (in != null) {
                in.close();
            }
            // 关闭连接
            if (httpsURLConnection != null) {
                httpsURLConnection.disconnect();
            }
        }
        return null;
    }

	/*
	 * @Test public void test() throws KeyManagementException,
	 * NoSuchAlgorithmException, NoSuchProviderException, IOException { String
	 * requestUrl = "https://tcc.taobao.com/cc/json/mobile_tel_segment.htm";
	 * Map<String, Object> params = new HashMap<String, Object>(); params.put("tel",
	 * "15311968005"); invokeSSLUrl(requestUrl, params, null,
	 * CustomerHttpMethod.GET, null); }
	 */
}

class MyHostnameVerifier implements HostnameVerifier {

    @Override
    public boolean verify(String arg0, SSLSession arg1) {
        return true;
    }

}

class MyX509TrustManager implements TrustManager {

}
